# -*- coding: utf-8 -*- 
"""
@Author : Chan ZiWen
@Date : 2022/7/20 08:26
File Description:

对表中所有数据进行更新，
"""
import logging
import time
import vthread
import json
import psutil
import requests
import numpy as np
import pandas as pd

from datetime import datetime
from clickhouse_driver import Client
from flask import jsonify, request, Blueprint, current_app

sleepWakeFinal = Blueprint('sleepWakeFinal', __name__)

interval_20 = 1728000
a_std = 45.
limit = 1000

# 计算线程数量
pc_mem = psutil.virtual_memory()
div_gb_factor = (1024 ** 2)
thread_n = int((pc_mem.available / div_gb_factor) // 20)
# 用于并行处理数据
pool1 = vthread.pool(thread_n - 1, gqueue=1)  # 开40个伺服线程，组名为1
# 用于后台执行处理
pool0 = vthread.pool(2, gqueue=0)  # 开40个伺服线程，组名为0


def post(datas, url, headers):
    datas = json.dumps(datas)
    response = json.loads(requests.post(url, data=datas, headers=headers).text)
    if response['code'] != 1000:
        raise RuntimeError(f" ({response})")


def save2ck(config_ck, update_datas):
    """
    ALTER TABLE UPDATE PageViews=7 WHERE UserID=4324182021466249700;
    """
    client = Client(
        host=config_ck['host'],
        database=config_ck['database'],
        user=config_ck['user'],
        password=config_ck['password'])

    sql_base = """insert into {table} FORMAT JSONEachRow {data_json}"""
    n = len(update_datas)
    if n > 0:
        for i in range(0, n, limit):
            sql = sql_base.format(table="life_time_day15",
                                  data_json=json.dumps(update_datas[i:i + limit]).replace("]", "").replace("[", ""))
            client.execute(sql)
    client.disconnect()


def func(timestamp: int):
    """ 提取 时分 """
    date_time = datetime.fromtimestamp(timestamp)
    hour = date_time.hour
    min = date_time.minute
    if hour < 3:
        hour += 24
    return hour * 60 + min


def minute_floor(x: int = None):
    if 0 <= x <= 8:
        x = 0
    elif x <= 15:
        x = 15
    elif x < 30:
        x = 15
    elif x < 45:
        x = 30
    else:
        x = 45
    return x


@pool1
def utils(update_datas, infos: list, res_days: list, analysis_date: str):
    # infos [mode, tv_mac, active_id, mac]
    """ 初始一个时间方便计算时间平均值 """
    if len(res_days) <= 1:
        return None
    elif len(res_days) == 2:
        # 提取 时分
        res_days = sorted(res_days)
        final_time = list(map(func, res_days))
        final_time = sum(final_time) // 2  # to integer
        hour, minute = (final_time // 60) % 24, final_time % 60
        ftime = f'{hour:>02d}:{minute_floor(minute):>02d}'
        final_time = hour * 60 + minute_floor(minute)
    else:
        # remove maximum and minimum
        res_days = sorted(res_days)
        if len(res_days) == 3:
            final_time = res_days[0]
            datetime_c = datetime.fromtimestamp(final_time)
            ftime = f'{datetime_c.hour:>02d}:{minute_floor(datetime_c.minute):>02d}'
            final_time = datetime_c.hour * 60 + minute_floor(datetime_c.minute)
        else:  # 取中位数
            # 提取 时分
            final_time = list(map(func, res_days))
            final_time = int(np.median(final_time))
            hour, minute = (final_time // 60) % 24, final_time % 60
            ftime = f'{hour:>02d}:{minute_floor(minute):>02d}'
            final_time = hour * 60 + minute_floor(minute)
    if final_time > 360:
        create_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        update_datas.append({
            'mode': int(infos[0]),
            'tv_mac': infos[1],
            'active_id': infos[2],
            'mac': infos[3],
            'final_15d_time': ftime,
            'analysis_datetime': str(analysis_date),
            'create_time': create_time
        })


def validate(date_text):
    try:
        datetime.strptime(date_text, '%Y-%m-%d')
        return 0
    except ValueError:
        return 1


@pool0
def run(date, config_ck):
    update_datas = []
    # define clickhouse connect
    client = Client(
        host=config_ck['host'],
        database=config_ck['database'],
        user=config_ck['user'],
        password=config_ck['password'])
    print(f"Begin of the ({date})")
    date_stamp = int(time.mktime(time.strptime(date, "%Y-%m-%d")))
    """ 根据 activeId 和 mac ，被扫描到的mac """
    sql = f"SELECT mode, tv_mac, active_id, mac, one_day_time, toString(analysis_datetime), toUnixTimestamp(CONCAT(CAST(analysis_datetime as String), ' 00:00:00')) as ut " \
          f"FROM life_time_day1 " \
          f"where ut >= {date_stamp - interval_20} and ut <= {date_stamp}"
    print(sql)
    data = client.execute(sql)
    client.disconnect()
    if len(data) <= 0:
        logging.warning(f'The length of {date} is {len(data)} ')
        return
    df_data = pd.DataFrame(data,
                           columns=['mode', 'tv_mac', 'active_id', 'mac', 'one_day_time', 'analysis_datetime', 'ut'])

    df_data = df_data.groupby(by=['mode', 'tv_mac', 'active_id', 'mac'])
    for id, data_t in df_data:
        utils(update_datas, id, data_t['one_day_time'].values, data_t['analysis_datetime'].values[-1])

    vthread.pool.wait(gqueue=1)
    # write to ck
    save2ck(config_ck, update_datas)
    print('save ok ')
    update_datas.clear()


@sleepWakeFinal.route('/final', methods=['GET', 'POST'])
def final():
    """
    http://172.20.148.11:8688/analyze/sleepWakeFinal/final?date=2022-07-02
    """
    # （merge 15d） clickhouse configures
    config_ck = {
        'host': current_app.config['CLICKHOUSE_HOST'],
        'port': current_app.config['CLICKHOUSE_PORT'],
        'user': current_app.config['CLICKHOUSE_USER'],
        'password': current_app.config['CLICKHOUSE_PASSWORD'],
        'database': current_app.config['CLICKHOUSE_DATABASE'],
       }

    register_dict = request.args
    date = register_dict['date']
    if validate(date):
        return jsonify({"status": "Incorrect data format, should be YYYY-MM-DD"}), 404
    run(date, config_ck)
    return jsonify({"status": 'success'}), 200